/**
* file name  : Jenoware/it/ciano/jenoware/PhenotypeFreq.java
* authors    : Luciano Xumerle
* created    : dom 20 nov 2005 17:13:51 CET
*
* Copyright (c) 2004-2006 Luciano Xumerle. All rights reserved.
*
* This file is part of gephinfo.
*
* gephinfo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/

package it.ciano.jenoware;

import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import java.util.Collections;
import it.ciano.util.CString;
import it.ciano.util.CCounter;

/**
 * The class manage information about a single phenotype stored into a Pedigree.
 * There is information about phenotype (trait or affection) and frequencies.
 * A phenotype is detected as a Traits if the constructor recognizes a valid
 * number. All the others values is treated as Unknown.
 * A phenotype is detected as an Affected traits if the values observed for a
 * phenotype are 1 and, 2, or 'no' and, 'yes', or 'n' and, 'y'.
 * Valid null value are the empty string, "0" and, "x".
 *
 * @author Luciano Xumerle
 * @version 0.0.1
 */
public class PhenotypeFreq
{
    // GLOBAL PRIVATE VARS
    private String _name;
    private String _type;

    // the counter
    private CCounter _counter;

    /**
     * The value corresponds to a trait phenotype.
     */
    final public static String TRAIT = "T";

    /**
     * The value corresponds to an affected phenotype.
     */
    final public static String AFFECTED = "A";

    /**
     * The values corresponds to a phenotype without valid elements.
     */
    final public static String NULL = "N";

    /**
     * Class Constructor.
     *
     * @param ped a Pedigree object.
     * @param phenotype a phenotype stored into the Pedigree object.
     */
    public PhenotypeFreq ( Pedigree ped, String phenotype )
    {
        // initializing private properties
        _name = phenotype;
        _counter = new CCounter();

        // parse the Pedigree objects and update counter
        Sbj[] ids = ped.getListSbj();
        for ( int id = 0; id < ids.length;id++ )
            _counter.inc ( ids[ id ].getPhenotype ( phenotype ).getValue() );

        /**
         * now we have the CCounter objects which contains all the values
         * available for the phenotype.
         * Now the program scans the values of phenotype and try to set the
         * correct phenotype type.
         */
        ArrayList number = new ArrayList();
        ArrayList notFloat = new ArrayList();
        String[] list = getObservedPhenotypes();
        for ( int i = 0; i < list.length; i++ )
        {
            if ( list[ i ].equals ( "" ) )
                continue;
            if ( CString.isFloat ( list[ i ] ) )
                number.add ( list[ i ] );
            else
                notFloat.add ( list[ i ].toLowerCase() );
        }

        if ( notFloat.size() == 0 )
        {
            Collections.sort ( number );
            if ( number.size() == 1 )
            {
                String first = ( String ) number.get ( 0 );
                if ( first.equals ( "1" ) || first.equals ( "2" ) )
                    _type = AFFECTED;
                else if ( first.equals ( "0" ) )
                    _type = NULL;
                else
                    _type = TRAIT;
            }
            else if ( number.size() == 2 )
            {
                String jj = ( String ) number.get ( 0 ) + ( String ) number.get ( 1 );
                if ( jj.equals ( "01" ) || jj.equals ( "02" ) || jj.equals ( "12" ) )
                    _type = AFFECTED;
                else
                    _type = TRAIT;
            }
            else if ( number.size() == 3 )
            {
                String jj = ( String ) number.get ( 0 )
                        + ( String ) number.get ( 1 ) + ( String ) number.get ( 2 );
                if ( jj.equals ( "012" ) )
                    _type = AFFECTED;
                else
                    _type = TRAIT;
            }
            else if ( number.size() > 3 )
                _type = TRAIT;
            else
                _type = NULL;
        }
        else
        {
            for ( int i = 0; i < number.size(); i++ )
                notFloat.add ( ( String ) number.get ( i ) );
            Collections.sort ( notFloat );
            // if value is 2 then the Phenotype may be an AFFECTED Phenotype
            if ( notFloat.size() == 2 )
            {
                String a = ( ( String ) notFloat.get ( 0 ) ).substring ( 0, 1 )
                        + ( ( String ) notFloat.get ( 1 ) ).substring ( 0, 1 );
                if ( a.equals ( "ny" ) || a.equals ( "ns" ) )
                    _type = TRAIT;
            }
            _type = NULL;
        }
    }

    /**
     * Returns the Phenotype name.
     *
     * @return the Phenotype name.
     */
    public String getPhenotype()
    {
        return _name;
    }

    /**
     * Returns the Phenotype type.
     *
     * @return the Phenotype type.
     */
    public String getType()
    {
        return _type;
    }

    /**
     * Returns true if the phenotype is a trait.
     *
     * @return true if the phenotype is a trait.
     */
    public boolean isTrait()
    {
        return _type.equals ( TRAIT );
    }

    /**
     * Returns true if the phenotype is affected.
     *
     * @return true if the phenotype is affected.
     */
    public boolean isAffected()
    {
        return _type.equals ( AFFECTED );
    }

    /**
     * Returns true if the phenotype has no valid values.
     *
     * @return true if the phenotype has no valid values.
     */
    public boolean isNull()
    {
        return _type.equals ( NULL );
    }

    /**
     * Returns the observed Phenotype values.
     *
     * @return the observed Phenotype values.
     */
    public String[] getObservedPhenotypes()
    {
        ArrayList res = new ArrayList();
        for ( Iterator it = _counter.entrySet().iterator(); it.hasNext(); )
        {
            Map.Entry eee = ( Map.Entry ) it.next();
            String t = ( ( String ) eee.getKey() );
            if ( t.equals ( "" ) )
                continue;
            res.add ( t );
        }
        Collections.sort ( res );
        return ( String[] ) res.toArray ( new String[res.size() ] );
    }

    /**
     * Overriding toString method.
     *
     * @return the descriptive statistics of the phenotype.
     */
    public String toString()
    {
        StringBuffer res = new StringBuffer();
        res.append ( "Phenotype name: " );
        res.append ( _name );
        res.append ( "\n\n" );
        res.append ( " -- type: " );
        res.append ( _type );
        res.append ( "\n" );
        if ( _type.equals ( AFFECTED )
                || (  _type.equals ( TRAIT )  && _counter.size() <10 ) )
        {
            int nullVal = 0;
            String[] list = _counter.getKeys();
            for ( int i = 0; i < list.length; i++ )
            {
                int val = _counter.getOccurrences ( list[ i ] );
                if ( list[ i ].equals ( "" ) || list[ i ].equals ( "0" ) )
                {
                    nullVal += val;
                }
                else
                {
                    res.append ( "  * " );
                    res.append ( list[ i ] );
                    res.append ( " : " );
                    res.append ( val );
                    res.append ( "\n" );
                }
            }
            res.append ( "  * 0 : " );
            res.append ( nullVal );
            res.append ( "\n" );
        }
        else
            res.append ( "\n\n" );
        return res.toString();
    }

} // end class

